package net.apexes.demo.codegen.util;

import net.apexes.commons.lang.Checks;
import net.apexes.commons.lang.SQLScriptRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;

import java.io.File;
import java.io.FileReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author <a href=mailto:hedyn@foxmail.com>HeDYn</a>
 */
public class SourceDatabaseCreator {

    private static final Logger LOG = LoggerFactory.getLogger(SourceDatabaseCreator.class);

    static {
        SLF4JBridgeHandler.removeHandlersForRootLogger();
        SLF4JBridgeHandler.install();
    }

    public static SourceDatabaseCreator h2database(String dbName) {
        return new SourceDatabaseCreator(dbName, null);
    }

    public static SourceDatabaseCreator postgres(String dbName) {
        return new SourceDatabaseCreator(dbName, "PostgreSQL");
    }

    private final String dbName;
    private final String mode;

    private SourceDatabaseCreator(String dbName, String mode) {
        Checks.verifyNotNull(dbName, "dbName");
        this.dbName = dbName;
        this.mode = mode;
    }

    public Connection forVersion(File dir, String version) throws Exception {
        Checks.verifyNotNull(dir, "dir");
        Checks.verifyNotNull(version, "version");

        File dbDir = new File(dir, dbName);
        File versionDir = new File(dbDir, version);
        List<File> sqlFileList = listFiles(versionDir);
        return initSourceDatabase(dbName, sqlFileList, mode);
    }

    public Connection forResource(File sqlFile) throws Exception {
        Checks.verifyNotNull(sqlFile, "sqlFile");
        return initSourceDatabase(dbName, Collections.singletonList(sqlFile), mode);
    }

    private static Connection initSourceDatabase(String dbName, List<File> sqlFileList, String mode) throws Exception {
        if (Checks.isEmpty(sqlFileList)) {
            throw new RuntimeException("No SQL script file.");
        }
        Connection conn = createConnection(dbName, mode);
        initDatabase(conn, sqlFileList);
        return conn;
    }

    private static void initDatabase(Connection conn, List<File> sqlFiles) throws Exception {
        for (File sqlFile : sqlFiles) {
            LOG.info("RunScript : {}", sqlFile.getAbsolutePath());
            try (FileReader reader = new FileReader(sqlFile)) {
                SQLScriptRunner runner = new SQLScriptRunner(conn, false, true);
                runner.runScript(reader);
            }
        }
    }

    private static Connection createConnection(String dbName, String mode) throws Exception {
        DriverManager.registerDriver(new org.h2.Driver());
        return DriverManager.getConnection(jdbcUrl(dbName, mode));
    }

    private static String jdbcUrl(String dbName, String mode) {
        if (Checks.isNotBlank(mode)) {
            return "jdbc:h2:mem:" + dbName + ";MODE=" + mode;
        }
        return "jdbc:h2:mem:" + dbName;
    }

    private static List<File> listFiles(File dir) {
        File[] files = dir.listFiles((file, name) -> name.endsWith(".sql"));
        if (files == null) {
            return Collections.emptyList();
        }
        return Arrays.asList(files);
    }
}
